package com.coocaa.aries.jmeter.service.impl;

import com.coocaa.aries.core.base.exception.AriesException;
import com.coocaa.aries.core.base.exception.ExceptionUtil;
import com.coocaa.aries.core.base.model.PageResultModel;
import com.coocaa.aries.core.base.model.R;
import com.coocaa.aries.core.base.mybatis.QueryTemplate;
import com.coocaa.aries.core.tool.InstanceUtil;
import com.coocaa.aries.core.tool.file.FileOpsUtil;
import com.coocaa.aries.core.tool.file.ZipFileUtil;
import com.coocaa.aries.core.tool.json.JsonUtil;
import com.coocaa.aries.core.tool.upload.model.UploadConfig;
import com.coocaa.aries.core.tool.upload.model.UploadResult;
import com.coocaa.aries.core.tool.upload.util.UploadUtil;
import com.coocaa.aries.jmeter.biz.impl.ExtTestCaseBizImpl;
import com.coocaa.aries.jmeter.entity.ExtTestCaseInfoEntity;
import com.coocaa.aries.jmeter.enums.ExecStatusEnums;
import com.coocaa.aries.jmeter.model.JmxTemplateModel;
import com.coocaa.aries.jmeter.model.JmxUploadResult;
import com.coocaa.aries.jmeter.model.query.ExtTestCaseQueryModel;
import com.coocaa.aries.jmeter.service.IExtTestCaseService;
import com.coocaa.aries.jmeter.service.IRunTimeProcessService;
import com.coocaa.aries.jmeter.thread.ThreadExecMgr;
import com.coocaa.aries.jmeter.util.*;
import com.coocaa.aries.jmeter.validator.FileValidator;
import com.coocaa.aries.jmeter.yml.JmeterProperties;
import com.coocaa.aries.jmeter.yml.PhantomJsProperties;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.Iterator;
import java.util.List;

/**
 * @author : bijiahao
 * @date : 2019/4/9.
 * @description: 测试用例 service接口实现
 * <p> </p>
 */
@Service
public class ExtTestCaseServiceImpl implements IExtTestCaseService, InitializingBean {
    private static final Logger log = LoggerFactory.getLogger(ExtTestCaseServiceImpl.class);

    @Autowired
    private JmeterProperties jmeterProperties;
    @Autowired
    private PhantomJsProperties phantomJsProperties;

    @Autowired
    private ExtTestCaseBizImpl extTestCaseBiz;
    @Autowired
    private IRunTimeProcessService runTimeProcessService;

    public ExtTestCaseBizImpl getExtTestCaseBiz() {
        return extTestCaseBiz;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        ExtTestCaseUtil.setExtTestCaseService(this);
    }

    /**
     * 分页数据
     *
     * @param model
     * @return
     */
    @Override
    public PageResultModel<ExtTestCaseInfoEntity> getPageResult(ExtTestCaseQueryModel model) {
        PageResultModel<ExtTestCaseInfoEntity> pageResult = extTestCaseBiz.getCommonPageResult(model);
        return pageResult;
    }

    /**
     * 获取执行日志
     *
     * @param id
     * @return
     */
    @Override
    public R getLog(Integer id) {
        if (null == CmdExecLogUtil.listLog(id)) {
            return R.ok("No Logs!");
        }
        Iterator<String> it = CmdExecLogUtil.listLog(id).iterator();
        StringBuilder log = new StringBuilder();
        while (it.hasNext()) {
            log.append(it.next())
                    .append("\n");
        }
        return R.ok(log.toString());
    }

    /**
     * 创建
     *
     * @param id
     * @return
     * @throws Exception
     */
    @Override
    public R create(Integer id) throws Exception {
        ExtTestCaseInfoEntity entity = extTestCaseBiz.queryById(id);
        String jmxPath = PathUtil.getAbsoluteJmxStorePath(entity, jmeterProperties);
        FileValidator.validFileExist(jmxPath);
        if (ExecStatusEnums.isExecuting(entity.getStatus())) {
            return R.error("当前正在生成报告");
        } else {
            removeProcessAndDeleteOld(entity);
        }
        ThreadExecMgr.offer(entity);
        if (ThreadExecMgr.isExecuing()) {
            extTestCaseBiz.updateStatus(id, ExecStatusEnums.QUEUING.getCode());
        }
        return R.ok();
    }

    /**
     * 取消
     *
     * @param id
     * @return
     */
    @Override
    public R cancel(Integer id) {
        ExtTestCaseInfoEntity entity = extTestCaseBiz.queryById(id);
        if (ExecStatusEnums.isExecuting(entity.getStatus())) {
            stopExecutingAndUpdateStatus(entity);
            ThreadExecMgr.set(false);
            return R.ok();
        }
        if (ExecStatusEnums.isQueuing(entity.getStatus())){
            stopExecutingAndUpdateStatus(entity);
            return R.ok();
        }
        return R.error("当前没有正在执行中！");

    }

    /**
     * 删除
     * @param id
     * @return
     */
    @Override
    public R remove(Integer id) {
        ExtTestCaseInfoEntity entity = extTestCaseBiz.queryById(id);
        if (ExecStatusEnums.isCanRemove(entity.getStatus())) {
            extTestCaseBiz.getMapper().deleteById(id);
            return R.ok();
        }
        return R.fail("当前状态不能删除");
    }

    /**
     *
     */
    @Override
    public void stopAllExecuting() {
        QueryTemplate<ExtTestCaseInfoEntity> wrapper = QueryTemplate.getEqQueryTemplate("`status`", ExecStatusEnums.EXECUING.getCode());
        List<ExtTestCaseInfoEntity> list = extTestCaseBiz.getMapper().selectList(wrapper);
        if (CollectionUtils.isNotEmpty(list)) {
            for (ExtTestCaseInfoEntity entity : list) {
                stopExecutingAndUpdateStatus(entity);
            }
            ThreadExecMgr.set(false);
        }
    }

    /**
     * 预览PDF测试报告
     *
     * @param response
     * @param id
     */
    @Override
    public void previewPdf(HttpServletResponse response, Integer id) {
        ExtTestCaseInfoEntity entity = extTestCaseBiz.queryById(id);
        String reportDir = PathUtil.getAbsoluteReportStoreDir(entity, jmeterProperties);
        String pdfFilePath = PathUtil.getReportPdfFilePath(reportDir);
        File pdfFile = new File(pdfFilePath);
        previewFile(response, pdfFile);
    }

    /**
     * 预览PDF前的校验
     *
     * @param id
     * @return
     */
    @Override
    public R checkBeforePreviewPdf(Integer id) {
        ExtTestCaseInfoEntity entity = extTestCaseBiz.queryById(id);
        if (StringUtils.isBlank(entity.getReportFileDir())) {
            return R.error("测试报告还没有生成");
        }
        if (StringUtils.isNotBlank(entity.getReportFileDir()) && ExecStatusEnums.isExecuting(entity.getStatus())) {
            return R.error("测试报告还在生成中");
        }
        String reportDir = PathUtil.getAbsoluteReportStoreDir(entity, jmeterProperties);
        String indexHtmlPath = PathUtil.getReportIndexHtmlFilePath(reportDir);
        FileValidator.validFileExist(indexHtmlPath);
        String pdfFilePath = PathUtil.getReportPdfFilePath(reportDir);
        File pdfFile = new File(pdfFilePath);
        if (!pdfFile.exists()) {
            try {
                FileUtils.forceMkdir(pdfFile.getParentFile());
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                boolean res = new PhantomJsCmdBuilder(phantomJsProperties).execHtml2Pdf(indexHtmlPath, pdfFilePath);
            } catch (Exception e) {
                log.error(ExceptionUtil.getStackTraceAsString(e));
                return R.error("生成PDF报告异常！").detailMsg(ExceptionUtil.getStackTraceAsString(e));
            }
        }
        return R.ok();
    }

    @Override
    public void downloadReport(HttpServletResponse response, Integer id) {
        ExtTestCaseInfoEntity entity = extTestCaseBiz.queryById(id);
        String reportDir = PathUtil.getAbsoluteReportStoreDir(entity, jmeterProperties);
        String zipFilePath = PathUtil.getReportZipFilePath(reportDir);
        File zipFile = new File(zipFilePath);
        response.reset();
        response.setCharacterEncoding("UTF-8");
        // 不同类型的文件对应不同的MIME类型
        response.setContentType("application/x-msdownload");
        // inline在浏览器中直接显示，不提示用户下载
        // attachment弹出对话框，提示用户进行下载保存本地
        // 默认为inline方式
        response.setHeader("Content-Disposition", "attachment;filename=" + zipFile.getName());
        previewFile(response, zipFile);
    }

    @Override
    public R checkBeforeDownReport(Integer id) {
        ExtTestCaseInfoEntity entity = extTestCaseBiz.queryById(id);
        if (StringUtils.isBlank(entity.getReportFileDir())) {
            return R.error("测试报告还没有生成");
        }
        if (StringUtils.isNotBlank(entity.getReportFileDir()) && ExecStatusEnums.isExecuting(entity.getStatus())) {
            return R.error("测试报告还在生成中");
        }
        String reportDir = PathUtil.getAbsoluteReportStoreDir(entity, jmeterProperties);
        String indexHtmlPath = PathUtil.getReportIndexHtmlFilePath(reportDir);
        FileValidator.validFileExist(indexHtmlPath);
        String zipFilePath = PathUtil.getReportZipFilePath(reportDir);
        File zipFile = new File(zipFilePath);
        if (!zipFile.exists()) {
            try {
                ZipFileUtil.zipFile(reportDir, zipFilePath);
            } catch (Exception e) {
                log.error(ExceptionUtil.getStackTraceAsString(e));
                return R.error("生成zip报告异常！").detailMsg(ExceptionUtil.getStackTraceAsString(e));
            }
        }
        return R.ok();
    }

    /**
     * 保存
     *
     * @param list
     */
    @Override
    public void save(List<ExtTestCaseInfoEntity> list) {
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        if (list.size() == 1 && list.get(0).getId() != null) {
            ExtTestCaseInfoEntity entity = list.get(0);
            ExtTestCaseInfoEntity entityDb = extTestCaseBiz.queryById(entity.getId());
            if (ExecStatusEnums.isQueuing(entityDb.getStatus())) {
                throw new AriesException("当前正在排队,不能编辑");
            }
            if (ExecStatusEnums.isExecuting(entityDb.getStatus())) {
                throw new AriesException("当前正在执行中,不能编辑");
            }
            JmxTemplateUtil.handleTemplate2File(entity, jmeterProperties);
            entity.setStatus(ExecStatusEnums.INIT.getCode());
            extTestCaseBiz.updateById(entity);
        } else {
            for (ExtTestCaseInfoEntity entity : list) {
                JmxTemplateUtil.handleTemplate2File(entity, jmeterProperties);
                entity.setStatus(ExecStatusEnums.INIT.getCode());
            }
            extTestCaseBiz.insertBatch(list);
        }
    }


    /**
     * 上传测试用例jmx文件
     *
     * @param request
     * @return
     */
    @Override
    public List<JmxUploadResult> uploadJmxFile(HttpServletRequest request) {
        UploadConfig uploadConfig = new UploadConfig();
        uploadConfig.setTypesLimit(".jmx");
        uploadConfig.setUploadDir(jmeterProperties.getJmxStorePath());
        List<UploadResult> list = UploadUtil.uploadTempFile(request, uploadConfig);
        List<JmxUploadResult> jmxUploadResultList = InstanceUtil.newArrayList(1);
        if (CollectionUtils.isNotEmpty(list)) {
            for (UploadResult uploadResult : list) {
                String jmxFilePath = PathUtil.getAbsoluteJmxStorePath(uploadResult.getNewFileName(), jmeterProperties);
                JmxTemplateModel model = JmxFileParseUtil.parseJmxModel(jmxFilePath);
                JmxUploadResult jmxUploadResult = new JmxUploadResult();
                BeanUtils.copyProperties(uploadResult, jmxUploadResult);
                jmxUploadResult.setJmxConfigJson(JsonUtil.obj2Json(model));
                jmxUploadResultList.add(jmxUploadResult);
            }
        }
        return jmxUploadResultList;
    }

    /**
     * 获取详情信息
     *
     * @param id
     * @return
     */
    @Override
    public ExtTestCaseInfoEntity getDetailInfo(Integer id) {
        return extTestCaseBiz.queryById(id);
    }

    /**
     * 验证Jmeter是否安装,可使用
     *
     * @return
     */
    @Override
    public String checkJmeterReady() {
        try {
            return CmdUtil.checkJmeterReady(PathUtil.getJmeterBinDir(jmeterProperties));
        } catch (Exception e) {
            e.printStackTrace();
            return e.getMessage();
        }
    }

    /**
     * 验证PhantomJs是否安装,可使用
     *
     * @return
     */
    @Override
    public String checkPhantomJsReady() {
        try {
            return CmdUtil.checkPhantomJsReady();
        } catch (Exception e) {
            e.printStackTrace();
            return e.getMessage();
        }
    }

    /*************** ===私有方法=== ********************* ===start=== ***************************************/

    private void stopExecutingAndUpdateStatus(ExtTestCaseInfoEntity entity) {
        this.removeProcessAndDeleteOld(entity);
        extTestCaseBiz.updateStatusAndReportDir(entity.getId(), "", ExecStatusEnums.INIT.getCode());
    }

    private void removeProcessAndDeleteOld(ExtTestCaseInfoEntity entity) {
        ThreadExecMgr.remove(entity.getId());
        runTimeProcessService.removeProcess(entity.getId());
        //删除之前的测试报告和中间结果文件
        deleteOldReportAndJtl(entity);
        //清理之前的执行日志
        CmdExecLogUtil.remove(entity.getId());
    }

    /**
     * 删除之前的测试报告和中间结果文件
     *
     * @param entity
     */
    private void deleteOldReportAndJtl(ExtTestCaseInfoEntity entity) {
        if (StringUtils.isNotBlank(entity.getReportFileDir())) {
            //删除生成的文件
            String jtlPath = PathUtil.getAbsoluteJtlStorePath(entity, jmeterProperties);
            String reportDir = PathUtil.getAbsoluteReportStoreDir(entity, jmeterProperties);
            //删除指定目录和指定文件
            FileOpsUtil.delete(reportDir, jtlPath);
        }
    }


    private void previewFile(HttpServletResponse response, File file) {
        try {
            FileInputStream inputStream = new FileInputStream(file);
            OutputStream out = response.getOutputStream();
            int b;
            byte[] buffer = new byte[1024];
            while ((b = inputStream.read(buffer)) != -1) {
                // 4.写到输出流(out)中
                out.write(buffer, 0, b);
            }
            inputStream.close();
            out.flush();
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private String getDownloadFileName(HttpServletRequest request, String downloadZipFileName) {
        try {
            if (request.getHeader("User-Agent").toLowerCase().indexOf("firefox") > 0) {
                downloadZipFileName = new String(downloadZipFileName.getBytes("GB2312"), "ISO-8859-1");
            } else {
                // 对文件名进行编码处理中文问题
                downloadZipFileName = java.net.URLEncoder.encode(downloadZipFileName, "UTF-8");
                // 处理中文文件名的问题
                downloadZipFileName = new String(downloadZipFileName.getBytes("UTF-8"), "GBK");
            }
            return downloadZipFileName;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return downloadZipFileName;
        }
    }
}
